package com.ruoyi.tools;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

/**
 * 给定2条航线，计算相似度.
 *
 * @author :yanlixiong
 * @date :2025-02-08 15:07:27
 */
public class FlightPathSimilarityTool {
    public static void main(String[] args) {
        /*double[][] path1 = generateRandomPath(10); // 生成10个点的航线1
        double[][] path2 = generateRandomPath(12); // 生成12个点的航线2

        double similarity = calculateDTWDistance(path1, path2);
        System.out.println("两条航线的相似度（DTW 距离）: " + similarity);*/

        List<CoordinateInfo> path1 = new ArrayList<>();
        path1.add(new CoordinateInfo(138.9453, 30.1));
        path1.add(new CoordinateInfo(138.6453, 30.2));
        path1.add(new CoordinateInfo(138.3453, 30.3));

        List<CoordinateInfo> path2 = new ArrayList<>();
        path2.add(new CoordinateInfo(138.7453, 30.15));
        path2.add(new CoordinateInfo(138.4453, 30.25));
        path2.add(new CoordinateInfo(138.1453, 30.35));

        double similarity = FlightPathSimilarityTool.calculateDTWDistance(path1, path2);
        System.out.println("航线相似度（DTW 距离）: " + similarity);
    }

    // 生成随机航线（经度在前，纬度在后）
    public static double[][] generateRandomPath(int length) {
        double[][] path = new double[length][2];
        Random random = new Random();
        double lon = 120.0 + random.nextDouble(); // 120°E ~ 121°E
        double lat = 30.0 + random.nextDouble();  // 30°N ~ 31°N

        for (int i = 0; i < length; i++) {
            lon += (random.nextDouble() - 0.5) * 0.1;
            lat += (random.nextDouble() - 0.5) * 0.1;
            path[i][0] = lon;  // 经度
            path[i][1] = lat;  // 纬度
        }
        return path;
    }


    /**
     * Calculate dtw distance double.
     *
     * @param coordinateInfo1 the coordinate info 1
     * @param coordinateInfo2 the coordinate info 2
     * @return the double
     * @author :yanlixiong
     * @date :2025-02-08 15:02:43
     */
    public static double calculateDTWDistance(List<CoordinateInfo> coordinateInfo1, List<CoordinateInfo> coordinateInfo2) {
        // 将 List<CoordinateInfo> 转换为 double[][]，格式为 {经度, 纬度}
        double[][] path1 = convertToPathArray(coordinateInfo1);
        double[][] path2 = convertToPathArray(coordinateInfo2);

        return calculateDTWDistance(path1, path2);
    }

    // 将 List<CoordinateInfo> 转换为 double[][] 数组
    private static double[][] convertToPathArray(List<CoordinateInfo> coordinates) {
        int size = coordinates.size();
        double[][] path = new double[size][2];

        for (int i = 0; i < size; i++) {
            path[i][0] = coordinates.get(i).getLongitude(); // 经度在前
            path[i][1] = coordinates.get(i).getLatitude();  // 纬度在后
        }
        return path;
    }

    /**
     * 计算相似度（DTW 计算）
     * 计算两条航线的DTW距离
     *
     * 使用动态时间规整（DTW）算法计算两条航线的最短匹配距离。
     * DTW 距离越小，说明两条航线越相似。.
     *
     * @param path1 the path 1
     * @param path2 the path 2
     * @return the double
     * @author :yanlixiong
     * @date :2025-02-08 14:55:38
     */
    public static double calculateDTWDistance(double[][] path1, double[][] path2) {
        int n = path1.length, m = path2.length;
        double[][] dtw = new double[n + 1][m + 1];

        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                double cost = haversineDistance(path1[i - 1], path2[j - 1]);
                if (i == 1 && j == 1) {
                    dtw[i][j] = cost;
                } else if (i == 1) {
                    dtw[i][j] = cost + dtw[i][j - 1];
                } else if (j == 1) {
                    dtw[i][j] = cost + dtw[i - 1][j];
                } else {
                    dtw[i][j] = cost + Math.min(dtw[i - 1][j], Math.min(dtw[i][j - 1], dtw[i - 1][j - 1]));
                }
            }
        }
        return dtw[n][m];
    }

    // 计算两点间的球面距离（Haversine 公式）
    public static double haversineDistance(double[] point1, double[] point2) {
        final double R = 6371.0; // 地球半径（单位：公里）
        double lon1 = Math.toRadians(point1[0]), lat1 = Math.toRadians(point1[1]);
        double lon2 = Math.toRadians(point2[0]), lat2 = Math.toRadians(point2[1]);

        double dLat = lat2 - lat1;
        double dLon = lon2 - lon1;
        double a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
                Math.cos(lat1) * Math.cos(lat2) *
                        Math.sin(dLon / 2) * Math.sin(dLon / 2);
        double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
        return R * c; // 返回公里数
    }
}

